package com.example.aop;

import com.example.annocation.RedssionLockAspect;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Component
@Aspect
@Order(0)
@Slf4j
public class RedssionLockHandler {

    @Autowired
    private RedissonClient redissonClient;

    // AOP 处理分布式锁逻辑
    @Around("@annotation(redssionLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedssionLockAspect redssionLock) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String key = redssionLock.key();
        String redssionKey = getRedssionKey(joinPoint, method, key);

        Object obj = null;
        int waitTime = redssionLock.waitTime();
        int leaseTime = redssionLock.leaseTime();
        TimeUnit timeUnit = redssionLock.unit();
        RLock lock = redissonClient.getLock(redssionKey);

        boolean lockFlag = leaseTime != -1 ? lock.tryLock(waitTime, leaseTime, timeUnit) :
                waitTime != -1 ? lock.tryLock(waitTime, timeUnit) : lock.tryLock();

        if (!lockFlag) {
            log.info("redssionKey{}，获取分布式锁失败", redssionKey);
            throw new RuntimeException("分布式锁正在处理中");
        }

        try {
            obj = joinPoint.proceed();
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        } finally {
            /*
             * isHeldByCurrentThread方法 和 isLocked方法的区别
             * isHeldByCurrentThread: 用于检查当前线程是否持有锁
             * isLocked: 用于检查锁是否被任何线程持有
             */
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return obj;
    }

    /**
     * 获取redis key
     *
     * @param joinPoint
     * @param signature
     * @param method
     * @param key
     * @return
     */
    private String getRedssionKey(ProceedingJoinPoint joinPoint, Method method, String key) {
        return StringUtils.isNotBlank(key) ? spelParseRedssionKey(joinPoint, method, key) : getMethodKey(method);
    }

    /**
     * 类名加方法名
     * @param method
     * @return
     */
    private String getMethodKey(Method method) {
        return method.getDeclaringClass() + "-" + method.getName();
    }

    /**
     * spel表达式解析key
     *
     * @param joinPoint
     * @param method
     * @param key
     * @return
     */
    private String spelParseRedssionKey(ProceedingJoinPoint joinPoint, Method method, String key) {
        Object[] args = joinPoint.getArgs();
        ExpressionParser spelExpressionParser = new SpelExpressionParser();
        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();

        String[] paramNames = discoverer.getParameterNames(method);
        EvaluationContext context = new StandardEvaluationContext();
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }

        Expression expression = spelExpressionParser.parseExpression(key);
        return expression.getValue(context, String.class);
    }

}
